<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            margin: 0;
            overflow: hidden
        }
    </style>
</head>

<body>
    <script type="module">

        import {
            Scene,
            WebGLRenderer,
            PerspectiveCamera,
            Object3D,
            BufferGeometry,
            BoxBufferGeometry,
            BufferAttribute,
            MeshBasicMaterial,
            InstancedMesh,
            InstancedBufferAttribute,
            DynamicDrawUsage,
            CanvasTexture
        } from 'https://unpkg.com/three@0.121.1/build/three.module.js';

        const rowCount = 36;
        const columnCount = 30;
        const layerCount = 3;

        const dummy = new Object3D();

        const camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
        camera.position.set(6, 7);
        camera.lookAt(0.3, 0, 0);

        var scene = new Scene();

        var geom = new BoxBufferGeometry();

        geom.computeVertexNormals();

        const canvas = document.createElement('canvas');

        const size = canvas.height = canvas.width = 32;
        let ctx = canvas.getContext('2d');
        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, size, size);
        ctx.clearRect(1, 1, size - 2, size - 2);
        const map = new CanvasTexture(canvas);
        map.anisotropy = 4;
        var material = new MeshBasicMaterial({ map });


        const mesh = new InstancedMesh(geom, material, rowCount * columnCount * layerCount);
        mesh.instanceMatrix.setUsage(DynamicDrawUsage);
        scene.add(mesh);

        var renderer = new WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(devicePixelRatio);
        renderer.setSize(innerWidth, innerHeight);
        document.body.appendChild(renderer.domElement);


        addEventListener('resize', onWindowResize, false);

        animate(0);

        function onWindowResize() {
            camera.aspect = innerWidth / innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(innerWidth, innerHeight);
        }

        function animate(time) {

            render(time / 1000);
            requestAnimationFrame(animate);
        }

        function render(time) {

            var i = 0;

            for (var x = 0; x < rowCount; x++) {

                const a = x / rowCount * Math.PI * 2;
                const X = Math.cos(a) / 2;
                const Z = Math.sin(a) / 2;
                const t = (time) % 1;

                for (var y = 0; y < layerCount; y++) {

                    const shift = y * Math.abs(Math.sin(x / 1.3)) +
                        Math.sin(x / 1.3) +
                        Math.cos(x / 1.7) - layerCount;

                    for (var z = 0; z < columnCount; z++) {

                        const t1 = Math.max(0, (3 - z) + time % 1 - shift);
                        const Y = y - Math.pow(t1, 5);

                        dummy.position.set(X * (z + 4 - t), Y, Z * (z + 4 - t));
                        dummy.rotation.y = -a;
                        dummy.scale.set(0.5, 1, 0.5)
                        dummy.updateMatrix();

                        mesh.setMatrixAt(i++, dummy.matrix);
                    }
                }
            }
            mesh.instanceMatrix.needsUpdate = true;
            scene.rotation.y = time / 10;
            renderer.render(scene, camera);
        }
    </script>
</body>

</html>